org	0x7c00	
[BITS 16]

BaseOfStack	equ	0x7c00  ;栈的起始地址

BaseOfLoader	equ	0x1000;loader 的  base基址
OffsetOfLoader	equ	0x00;loader的代码偏移量，用于实模式  基地址<<4位+offset

RootDirSectors	equ	14  ;根目录的扇区数,大小
SectorNumOfRootDirStart	equ	19 ;根目录的起始编号
SectorNumOfFAT1Start	equ	1 ;FAT1表的起始编号
SectorBalance	equ	17	;

	jmp	short Func_Start
	nop
	BS_OEMName	db	'XOSboot'
    nop
	BPB_BytesPerSec	dw	512
	BPB_SecPerClus	db	1
	BPB_RsvdSecCnt	dw	1
	BPB_NumFATs	db	2
	BPB_RootEntCnt	dw	224
	BPB_TotSec16	dw	2880
	BPB_Media	db	0xf0
	BPB_FATSz16	dw	9
	BPB_SecPerTrk	dw	18 ;每个磁道的扇区数
	BPB_NumHeads	dw	2
	BPB_HiddSec	dd	0
	BPB_TotSec32	dd	0
	BS_DrvNum	db	0
	BS_Reserved1	db	0
	BS_BootSig	db	0x29
	BS_VolID	dd	0
	BS_VolLab	db	'boot loader'
	BS_FileSysType	db	'FAT12   '

Func_Start:

    mov ax,cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, BaseOfStack  ;设置堆栈为0x7C00







Func_ClearScreen:  ;ah=06
    mov ax, 0600h  ;al = 0 清屏，其余参数不用理
    mov bx, 0700h
    mov cx,0
    mov dx, 0184fh
    int 10h



Func_SetFocus:  ;ah = 02
    mov ax, 200h ;
    mov bx, 0000h ;行号
    mov dx, 0000h ;列号
    int 10h

Func_DisplayChar :  ;ah = 0x13
    mov ax,1301h   ;al = 01,显示后，光标会随着移动
    mov bx,000fh  ;BH 页码  BL 字符颜色属性
    mov cx, 10h  ;cx = 字符串长度
    push ax  
    mov ax, ds
    mov es,ax   ;es = 内存段地址
    pop ax
    mov bp, StartBootMessage  ;bp = 内存地址
    int 10h




Func_ResetFlopy:
    xor ah, ah ;重置磁盘驱动器
    xor dl, dl ;dl为驱动器号，00H~7FH为软盘， 80H~FFH为硬盘
    int 13h


;========================search Root Dir For Loader Bin String==================
    mov word[SectorNo],SectorNumOfRootDirStart  ;获取根目录其实开始扇区

;功能: 读取根目录的扇区到指定的00H:8000H
Lable_Search_In_Root_Dir_Begin:
    cmp word[RootDirSizeForLoop], 0 
    jz Label_No_LoaderBin   ;如果根目录大小为0,表示没有Loader.bin
    dec word[RootDirSizeForLoop] ;根目录大小-1
    mov ax, 00h
    mov es, ax
    mov bx, 8000h ;es:bx = 00:8000
    mov ax, [SectorNo] ;
    mov cl, 1
    call Func_ReadOneSector
    mov si, LoaderFileName ;目标目录的名字
    mov di, 8000h
    cld  ;关闭中断
    mov dx, 10h ;一个扇区有16个更目录表项
 
;比较根目录表项的名字，如果不同,就查找下一个表项,查找完毕都没找到
;读取下一个扇区
Label_Search_For_LoaderBin:
    cmp dx, 0
    jz Label_Goto_Next_Sector_In_Root_Dir;此扇区查找完毕,继续查找下一个扇区
    dec dx
    mov cx, 11
Label_Cmp_FileName:
    cmp cx, 0
    jz Label_FileName_Found
    dec cx
    lodsb;从字符串加载一个字符
    cmp al, byte [es:di]
    jz Label_Go_On
    jmp Label_Different
Label_Go_On:
    inc di
    jmp Label_Cmp_FileName
Label_Different:
    and di,0ffe0h
    add di, 20h
    mov si, LoaderFileName
    jmp Label_Search_For_LoaderBin

;增加搜索的开始扇区
Label_Goto_Next_Sector_In_Root_Dir:
    add word[SectorNo], 1
    jmp Lable_Search_In_Root_Dir_Begin


;ah = 0x13
;功能:打印目标内存的字符串，并设置了字符闪烁效果
Label_No_LoaderBin:
    mov ax, 1301h
    mov bx, 008ch
    mov dx, 0100h
    mov cx, 21
    push ax
    mov ax, ds
    mov es, ax
    pop ax
    mov bp, NoLoaderMessage
    int 10h
    jmp $

;==================================found loader.bin name in root director struct====================



Label_FileName_Found:

	mov	ax,	RootDirSectors ;获取根目录的扇区数
	and	di,	0ffe0h  
	add	di,	01ah 
	mov	cx,	word	[es:di]  ;获取loader bin 所在簇号
	push	cx  ;保存
	add	cx,	ax
	add	cx,	SectorBalance  ;计算扇区号 = 簇号 + 19 + 14 - 2
	mov	ax,	BaseOfLoader
	mov	es,	ax
	mov	bx,	OffsetOfLoader
	mov	ax,	cx

Label_Go_On_Loading_File:
	push	ax
	push	bx
	mov	ah,	0eh  ;ah = 0e
	mov	al,	'.'  ;al = 要显示的字符
	mov	bl,	0fh  ;bl = 前景色
	int	10h
	pop	bx
	pop	ax  ;打印字符

	mov	cl,	1
	call	Func_ReadOneSector
	pop	ax
	call	Func_GetFATEntry
	cmp	ax,	0fffh
	jz	Label_File_Loaded
	push	ax
	mov	dx,	RootDirSectors
	add	ax,	dx
	add	ax,	SectorBalance
	add	bx,	[BPB_BytesPerSec] ;增加512字节
	jmp	Label_Go_On_Loading_File


Label_File_Loaded:
	jmp	BaseOfLoader:OffsetOfLoader




;=================================Functions=================================
    

;AX=待读取的磁盘的扇区号
;CL=读入的扇区数量
;ES:BX=目标缓冲区
;功能:将LBA转换为CHS
Func_ReadOneSector:
    push bp
    mov bp, sp
    sub esp, 2 ;
    mov byte [bp - 2] ,cl ;保存读取次数
    push bx ;保存目标地址
    mov bl, [BPB_SecPerTrk]
    div bl  ;AX / BL  扇区数/每个磁道的扇区数
    inc ah  ;R+1 等于开始扇区号
    mov cl, ah;cl=扇区号
    mov dh, al;dh=磁头号
    shr al, 1  ;计算磁柱
    mov ch, al ;ch=磁柱的低8位
    and dh, 1 ;磁头号  因为只有0 和 1两个选项
    pop bx ;数据缓冲区
    mov dl, [BS_DrvNum] ;驱动器号
Label_Go_On_Reading:
    mov ah, 2 ;2号功能
    mov byte al, byte [bp - 2];al=读入的扇区数
    int 13h ;读取成功,CF标志位被复位
    jc Label_Go_On_Reading 
    add esp, 2
    pop bp
    ret

;AH=FAT表项编号
;return:下一个表项的编号
;功能:获取传入FAT表项地址的下一个FAT表项地址
Func_GetFATEntry:

	push	es
	push	bx
	push	ax
	mov	ax,	00
	mov	es,	ax
	pop	ax
	mov	byte	[Odd],	0
	mov	bx,	3
	mul	bx
	mov	bx,	2
	div	bx
	cmp	dx,	0 ;判断表项是奇还是偶
	jz	Label_Even
	mov	byte	[Odd],	1

Label_Even:

	xor	dx,	dx
	mov	bx,	[BPB_BytesPerSec]
	div	bx
	push	dx
	mov	bx,	8000h
	add	ax,	SectorNumOfFAT1Start ;表项扇区号
	mov	cl,	2
	call	Func_ReadOneSector ;读取两个扇区
	
	pop	dx 
	add	bx,	dx ;
	mov	ax,	[es:bx] ;获取读取数据的前16字节
	cmp	byte	[Odd],	1
	jnz	Label_Even_2
	shr	ax,	4  ;如果是奇数，就取高12字节,否则就直接返回

Label_Even_2:
	and	ax,	0fffh
	pop	bx
	pop	es
	ret
    RootDirSizeForLoop	dw	RootDirSectors ;根目录大小
    SectorNo		dw	0  ;根目录扇区开始地址
    Odd			db	0

    NoLoaderMessage:	db	"ERROR:No LOADER Found"
    LoaderFileName:		db	"LOADER  BIN",0

    StartBootMessage db "Start Booting..."
    times 510 - ($ - $$) db 2
    dw 0xaa55


